軽率多相(Levity Polymorphism)
種の使用方法に完全な柔軟性を持たせるためには,種システムを使用して,BoxedかつLiftedな型(Intや[Bool]などの通常で平凡な型)と,Int#のようなUnboxedかつプリミティブな型(Unboxed型とプリミティブ操作)を区別する必要がある.ゆえに我々は軽率多相(levity polymorphism)と呼ばれるものを持っている. 以下はGHC.Extsから入手可能な主要な定義である.
code:lp01.hs
TYPE :: RuntimeRep -> Type -- highly magical, built into GHC
data RuntimeRep = LiftedRep -- for things like Int
| UnliftedRep -- for things like Array#
| IntRep -- for Int#
| TupleRep RuntimeRep -- unboxed tuples, indexed by the representations of the elements | SumRep RuntimeRep -- unboxed sums, indexed by the representations of the disjuncts | ...
type Type = TYPE LiftedRep -- Type is just an ordinary type synonym
要するに,RuntimeRepによってパラメータ化される新しい基本型定数TYPEがあるということだ.したがって,Int# :: TYPE 'IntRep およびBool :: TYPE 'LiftedRep が得られる.TYPE xという形式の型を持つものはすべて,関数矢印->の両側に現れることができる.つまり,->はTYPE r1 -> TYPE r2 -> TYPE 'LiftedRepという型を持つということがいえる.GHCではすべての関数はLiftedになるため,その結果は常にLiftedになる.
軽率多相変数や引数はない
GHCが現実世界で動作するプログラムをコンパイルする必要がなければ,それで話は終わりだ.しかしはRepresentation Polymorphism(訳注:わからん)はGHCのコードジェネレータにかなりの問題を引き起こす可能性がある.以下の例を考てみよう.
code:lp02.hs
bad :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
(a :: TYPE r1) (b :: TYPE r2).
(a -> b) -> a -> b
bad f x = f x
これは一見して標準の$演算子の一般化のように見える.しかし,これを実行可能なコードにコンパイルすることを考えた場合,問題が発生する.特に,badを呼ぶときには,どうにかしてxをbadに渡さなければならない.xの幅(つまりビット数はどれくらいだろうか?それはポインタだろうか?どのような種類のレジスタ(浮動小数点または整数)にxを入れるべきか?xの型,a :: TYPE r1は軽率多相なので,これらの疑問に答えることは不可能である.そのため,次のような簡単な規則により,このような構成を禁止する.
どの変数も軽率多相な型を持つことはできない.
これは,変数xがRepresentation Polymorhic型を持つことになるので,badは違法である.
しかし,すべてが失われるわけではない.我々はまだ以下のようなコードを書くことができる.
code:lp03.hs
($) :: forall r (a :: Type) (b :: TYPE r).
(a -> b) -> a -> b
f $ x = f x
ここでは,bのみが軽率多相である.軽率多相な型を持つ変数はない.そしてコードジェネレータはこれで問題ない.実際に,これはGHCの$演算子の本当の型で,Haskell 98バージョンよりも少し一般的である.
コードジェネレータは変数だけでなく引数もストアおよび移動する必要があるため,上記のロジックは関数の引数にも同様に適用され,関数の引数も軽率多相になることができない.
軽率多相ボトム
我々は軽率多相を error とundefinedに関して効果的に用いることができる.これらの型は以下の通りである.
code:lp04.hs
undefined :: forall (r :: RuntimeRep) (a :: TYPE r).
HasCallStack => a
error :: forall (r :: RuntimeRep) (a :: TYPE r).
HasCallStack => String -> a
これらの関数は軽率多相な変数を束縛しないので受け入れられる.その多相性により,ユーザはこれらを使用してUnboxed型を返すスタブの関数を作ることができ便利である.
軽率多相型のプリンティング
-fprint-explicit-runtime-reps
RuntimeRepパラメータを表示どおりにプリントする.そうでなければ,それらはデフォルトで'LiftedRepに設定される.
ほとんどのGHCユーザは軽率多相やUnboxed型について心配する必要はない.これらのユーザにとっては,$の型の軽率多相を見ることは役に立たない.したがって,デフォルトでは,プリントするときにはRuntimeRep型のすべての型変数は'LiftedRepになると仮定し,TYPE 'LiftedRepをType(またはStarIsTypeが有効であれば,*)としてプリントすることによって,これは抑制されている.
あなたの型の軽率多相を見たいのなら,フラグ-fprint-explicit-runtime-repsを有効にせよ.